#include "Engine.h"

Engine::Engine(HINSTANCE hInstance, std::string window_class)
{
	this->hInstance = hInstance;
	this->window_class = window_class;
	this->window_class_wide = std::wstring(this->window_class.begin(), this->window_class.end());
	if (!RegisterWindowClass())
		exit(-1);
}

Engine::~Engine()
{
	if (!UnregisterClass(
#ifdef UNICODE
		this->window_class_wide.c_str(),
#else
		this->window_class.c_str(),
#endif // UNICODE
		hInstance))
	{
		ErrorLogger::Log(GetLastError(), "UnregisterClass");
	}
}

bool Engine::Initialize(std::string window_title, int width, int height, int nCmdShow)
{
	this->width = width;
	this->height = height;
	this->window_title = window_title;
	this->window_title_wide = std::wstring(this->window_title.begin(), this->window_title.end());

	if (!CreateNewWindowShow(nCmdShow))
		return false;
	return true;
}


bool Engine::RegisterWindowClass()
{
	WNDCLASSEX wc;
	ZeroMemory(&wc, sizeof(WNDCLASSEX));
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc = HandleMessageSetup;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = this->hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
	wc.lpszMenuName = NULL;
	wc.hIconSm = NULL;
#ifdef UNICODE
	wc.lpszClassName = this->window_class_wide.c_str();
#else
	wc.lpszClassName = this->window_class.c_str();
#endif // UNICODE

	if (!RegisterClassEx(&wc))
	{
		ErrorLogger::Log(GetLastError(), "RegisterClassEx");
		return false;
	}
	return true;
}

bool Engine::CreateNewWindowShow(int nCmdShow)
{
	CreateNewWindow();
	OnStart();
	ShowWindow(handle, nCmdShow);
	UpdateWindow(handle);
	SetForegroundWindow(handle);
	SetFocus(handle);
	return true;
}


bool Engine::CreateNewWindow()
{
	if (handle)
	{
		DestroyWindow(handle);
		handle = NULL;
	}
	int m_nWindwMetricsX = ::GetSystemMetrics(SM_CXSCREEN);
	int m_nWindwMetricsY = ::GetSystemMetrics(SM_CYSCREEN);

	RECT wr; //Window Rectangle
	wr.left = (m_nWindwMetricsX - width) / 2;
	wr.top = (m_nWindwMetricsY - height) / 2;
	wr.right = wr.left + width;
	wr.bottom = wr.top + height;
	AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

	handle = CreateWindowEx(0,
#ifdef UNICODE
		this->window_class_wide.c_str(),
		this->window_title_wide.c_str(),
#else
		this->window_class.c_str(),
		this->window_title.c_str(),
#endif // UNICODE
		WS_OVERLAPPEDWINDOW,
		wr.left,
		wr.top,
		wr.right - wr.left,
		wr.bottom - wr.top,
		NULL,
		NULL,
		hInstance,
		this
	);
	if (!handle)
	{
		ErrorLogger::Log(GetLastError(), "CreateWindowEx Failed for window:" + this->window_title);
		return false;
	}
	return true;
}

LRESULT CALLBACK Engine::HandleMsgRedirect(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	Engine* const pWindow = reinterpret_cast<Engine*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
	return pWindow->WindProc(hwnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK Engine::HandleMessageSetup(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_NCCREATE:
	{
		const CREATESTRUCTW* const pCreate = reinterpret_cast<CREATESTRUCTW*>(lParam);
		Engine* engine = reinterpret_cast<Engine*>(pCreate->lpCreateParams);
		if (!engine)
		{
			ErrorLogger::Log(GetLastError(), "Critical Error: Pointer to window container is null during WM_NCCREATE.");
			exit(-1);
		}
		SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(engine));
		SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(HandleMsgRedirect));
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}

LRESULT CALLBACK Engine::WindProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CLOSE:
		DestroyWindow(hwnd);
		this->handle = nullptr;
		OnClose();
		return 0;
	case WM_SIZE:
	{
		RECT rect;
		GetClientRect(hwnd, &rect);
		OnResize(rect.right - rect.left, rect.bottom - rect.top);
		return 0;
	}
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}

}


bool Engine::ProcessMessages()
{
	MSG msg;
	ZeroMemory(&msg, sizeof(MSG));
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	if (msg.message == WM_NULL)
	{
		if (!IsWindow(handle))
			return false;
	}
	if (!handle)
		return false;
	return true;
}

HWND Engine::GetHWND()
{
	return handle;
}

void Engine::Close()
{
	SendMessage(handle, WM_CLOSE, 0, 0);
}

void Engine::OnStart()
{

}


void Engine::OnClose()
{

}

void Engine::OnResize(int width, int height)
{

}
